<template>
  <div ref="canvas"></div>
</template>

<script lang="ts" setup>

/*
有一个场景 scene，场景里面有光线，有雾，有模型Mesh(Mesh模型 - 由几何对象Geometry，材质material组成)...
通过 scene 的 add 方法将 Mesh模型 添加到场景中
然后通过 渲染器WebGLRenderer 使用 相机camera 把这个场景scene 拍下来，展示到页面上

chapter-03 - 光源:
   THREE.AmbientLight光源的颜色可以附加到场景中的每一种颜色上，通常用来柔化生硬的颜色和阴影。
   THREE.PointLight光源会朝所有方向发射光线，不能被用来创建阴影。
   THREE.SpotLight光源类似于手电筒。它有一个锥形的光束，可以配置它随着距离的增大而逐渐变弱，并且可以生成阴影。
   THREE.DirectionalLight光源。这个光源相当于远光的效果，比如太阳光。它的光线彼此平行，其光强并不会随着与目标对象距离的增大而减弱。
   如果想要一个更加自然的户外效果，可以使用THREE.HemisphereLight光源，它考虑了天空和地面的反射。
   THREE.AreaLight不从单个点发射光线，而是从一个很大的区域发射光线。
   还可以通过THREE.LensFlare对象添加图像化的镜头光晕
chapter-04 - 材质:
   MeshBasicMaterial(网格基础材质): 基础材质, 用于给几何体赋予一种简单的颜色
   MeshDepthMaterial(网格深度材质): 根据从相机到网格的距离来决定如何给网格上色
   MeshNormalMaterial(网格法向材质): 根据法向向量计算物体表面的颜色
   MeshLambertMaterial(网格兰伯特材质): 受到光照影响的材质，多用于创建暗淡的，不光亮的物体
   MeshPhongMaterial(网格高亮材质): 受到光照影响的材质，多用于创建高亮的物体
   MeshStandardMaterial(网格标准材质): 使用基于物理的渲染(PBR)算法来绘制物体表面，能够计算出物体表面与光线的正确互动关系，从而使物体看起来更加的真实
   MeshPhysicalMaterial(网格物理材质): 这是 MeshStandardMaterial 的扩展材质，为光线反射计算模型提供了更多的控制
   MeshToonMaterial(网格卡通材质): 这是 MeshPhongMaterial 的扩展材质，使得物体渲染更加卡通化
   ShadowMaterial(阴影材质): 这是一个专门用于接受阴影图的特殊材质。在该材质中只有阴影图像，非阴影部分为完全透明的区域
   ShaderMaterial(着色器材质): 该材质允许使用自定义的着色器程序，直接控制顶点的放置方式和像素的着色方式
   LineBasicMaterial(直线基础材质): 可以用于 THREE.Line 几何体，用来创建着色的直线
   LineDashMaterial(虚线材质): 和 LineBasicMaterial 差不多，但允许创建出一种虚线的效果

* */

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GUI }  from "three/examples/jsm/libs/lil-gui.module.min.js";
import Stats from "three/examples/jsm/libs/stats.module";
import { ref, onMounted } from "vue";

// 画布
const canvas = ref<any>('');
// 场景
const scene = new THREE.Scene();
// 相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(-30, 40, 30);
camera.lookAt(scene.position);
// 平面
const planeGeometry = new THREE.PlaneGeometry(60, 20);
const planeMaterial = new THREE.MeshLambertMaterial({
  color: 0xAAAAAA,
  side: THREE.DoubleSide,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -0.5 * Math.PI;
plane.position.set(15, 0, 0);
plane.receiveShadow = true;
// 正方体
const boxGeometry = new THREE.BoxGeometry(4, 4, 4);
const boxMaterial = new THREE.MeshLambertMaterial({
  color: 0xff0000,
});
const box = new THREE.Mesh(boxGeometry, boxMaterial);
box.position.set(-4, 3, 0);
box.castShadow = true;
// 球体
const sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
const sphereMaterial = new THREE.MeshLambertMaterial({
  color: 0x7777ff,
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.set(20, 4, 2);
sphere.castShadow = true;
// 添加进场景
scene.add(plane, box, sphere);
// 光源
const spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 40, -15);
spotLight.castShadow = true;
spotLight.shadow.mapSize = new THREE.Vector2(window.innerWidth, window.innerHeight);
spotLight.shadow.camera.far = 130;
spotLight.shadow.camera.near = 40;
scene.add(spotLight);
// 渲染器
const renderer = new THREE.WebGLRenderer({
  antialias: true,
});
renderer.setClearColor(0xffffff);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// 相机控件
const orbitControl = new OrbitControls(camera, renderer.domElement);
orbitControl.update();
// 性能统计
const stat = Stats();
stat.setMode(0);
// gui界面
const gui = new GUI();
/**
 * 渲染函数
 */
const render = () => {
  stat.update();

  renderer.render(scene, camera);
  requestAnimationFrame(render);
}

onMounted(() => {
  canvas.value.appendChild(renderer.domElement);
  canvas.value.appendChild(stat.domElement);
  render();
})
</script>

<style scoped>
body{
  margin: 0;
  overflow: hidden;
}
</style>
